#include <assert.h>
#include <cassert>
#include <iomanip>
#include <iostream>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string.h>

#include "../headers/CPU.h"
#include "../headers/FieldSpecifications.h"
#include "../headers/OpCodes.h"
#include "../headers/Helpers.h"
#include "../headers/CpuHalted.h"
#include "../headers/AddressOutOfRangeException.h"

using namespace std;

CPU::CPU(void)
{
	this->initialize();
}


CPU::~CPU(void)
{
	// clean op code names
	for(map < OpCodes, pair < unsigned int, string* > >::iterator it = this->executionTimesAndNames.begin(); it != this->executionTimesAndNames.end(); it++)
	{
		delete[] it->second.second;
	}

	this->executionTimesAndNames.clear();

	for(unsigned int n = 0; n < CPU::MEMORY_SIZE; n++)
	{
		delete this->pMemory[n];
	}

	delete[] this->pMemory;
}


CPU* CPU::clone() const
{
	CPU* result = new CPU();
	result->comparison = this->comparison;
	result->overflowToggle = this->overflowToggle;

	for(unsigned int n = 0; n < CPU::MEMORY_SIZE; n++)
	{
		result->pMemory[n]->value(this->pMemory[n]->value_according_to_field_specification(L0_R5));
	}

	// Words
	result->registerA.value(this->registerA.value_according_to_field_specification(L0_R5));
	result->registerX.value(this->registerX.value_according_to_field_specification(L0_R5));

	// SmallWords
	result->registerJ.value(this->registerJ.value_according_to_field_specification(L0_R2));
	for(unsigned int n = 0; n < CPU::INDEX_REGISTER_COUNT; n++)
	{
		result->registerI[n].value(this->registerI[n].value_according_to_field_specification(L0_R2));
	}

	result->ticks = this->ticks;
	result->instructionPointer = this->instructionPointer;

	return result;
}


Word* CPU::memory(const unsigned int address) const
{
	Word* result = NULL;
	if(address < CPU::MEMORY_SIZE)
	{
		result = this->pMemory[address];
	}

	return result;
}


Comparison CPU::comparison_indicator() const
{
	return this->comparison;
}


bool CPU::overflow() const
{
	return this->overflowToggle;
}


void CPU::dump_core(std::ostream* const os) const
{
	(*os) << "Core Dump of CPU @ 0x" << this << endl << endl;

	(*os) << "IP: 0x" << std::hex << std::setfill('0') << std::setw(ADDRESS_WIDTH) << this->instructionPointer << endl << endl;

	(*os) << "rA: ";
	this->registerA.write_word(os);
	(*os) << "     ";
	(*os) << "rX: ";
	this->registerX.write_word(os);
	(*os) << endl;
	(*os) << "Overflow Toggle:      " << this->overflowToggle << endl;
	(*os) << "Comparison Indicator: ";
	pretty_print_comparison(os, this->comparison);
	(*os) << endl;

	for(unsigned int n = 0; n < CPU::INDEX_REGISTER_COUNT; n++)
	{
		if(n % 3 == 0)
		{
			(*os) << endl;
		}

		(*os) << "I" << n + 1 << ": ";
		this->registerI[n].write_word(os);
		(*os) << "  ";
	}
	(*os) << endl;

	(*os) << "J:  ";
	this->registerJ.write_word(os);
	(*os) << endl << endl;

	(*os) << "Memory @ 0x" << this->pMemory << " (" << CPU::MEMORY_SIZE << " MIX words)" << endl;
	for(unsigned int n = 0; n < CPU::MEMORY_SIZE; n++)
	{
		if(n % 8 == 0)
		{
			(*os) << endl;
		}

		if(n != this->instructionPointer)
		{
			(*os) << ' ';
		}
		else
		{
			(*os) << '>';
		}

		(*os) << hex << setfill('0') << setw(ADDRESS_WIDTH) << n << ":";
		this->pMemory[n]->write_word(os, false);

		if(n != this->instructionPointer)
		{
			(*os) << ' ';
		}
		else
		{
			(*os) << '<';
		}

		(*os) << " ";
	}

	(*os) << dec << endl;
}


void CPU::initialize()
{
	this->registerSerializationSequence[0] = &this->registerA;
	this->registerSerializationSequence[1] = &this->registerX;
	for(unsigned int n = 2; n < CPU::INDEX_REGISTER_COUNT + 2; n++)
	{
		this->registerSerializationSequence[n] = &this->registerI[n - 2];
	}

	this->registerSerializationSequence[CPU::INDEX_REGISTER_COUNT + 2] = &this->registerJ;

	this->populate_execution_times();

	this->pMemory = new Word*[CPU::MEMORY_SIZE];
	for(unsigned int n = 0; n < CPU::MEMORY_SIZE; n++)
	{
		this->pMemory[n] = new Word();
	}

	this->comparison = Equal;
	this->overflowToggle = false;

	this->ticks = 0;
	this->instructionPointer = 0;
}


const Word* const CPU::instruction_word() const
{
	Word* result = NULL;
	if(this->instructionPointer < CPU::MEMORY_SIZE)
	{
		result = this->pMemory[this->instructionPointer];
	}
	else
	{
		AddressOutOfRangeException ex;
		ex.memory_size = CPU::MEMORY_SIZE;
		ex.requested_address = this->instructionPointer;

		throw ex;
	}

	return result;
}


unsigned int CPU::instruction_pointer() const
{
	return this->instructionPointer;
}


void CPU::instruction_pointer(const unsigned int address)
{
	if(address < CPU::MEMORY_SIZE)
	{
		this->instructionPointer = address;
	}
	else
	{
		AddressOutOfRangeException ex;
		ex.memory_size = CPU::MEMORY_SIZE;
		ex.requested_address = address;

		throw ex;
	}
}


void CPU::load_memory(ifstream* const fs)
{
	// load the memory
	for(unsigned int n = 0; n < CPU::MEMORY_SIZE; n++)
	{
		this->pMemory[n]->deserialize(fs);
	}

	// instruction pointer
	fs->read((char*)&this->instructionPointer, sizeof(unsigned int));

	// ticks pointer
	fs->read((char*)&this->ticks, sizeof(unsigned int));

	// All Registers
	for(unsigned int n = 0; n < CPU::TOTAL_REGISTER_COUNT; n++)
	{
		this->registerSerializationSequence[n]->deserialize(fs);
	}

	// overflow toggle
	fs->read((char*)&this->overflowToggle, sizeof(bool));

	// Comparison
	fs->read((char*)&this->comparison, sizeof(Comparison));
}


void CPU::store_memory(ofstream* const fs)
{
	// store the memory
	for(unsigned int n = 0; n < CPU::MEMORY_SIZE; n++)
	{
		this->pMemory[n]->serialize(fs);
	}

	// instruction pointer
	fs->write((char*)&this->instructionPointer, sizeof(unsigned int));

	// ticks
	fs->write((char*)&this->ticks, sizeof(unsigned int));

	// All Registers
	for(unsigned int n = 0; n < CPU::TOTAL_REGISTER_COUNT; n++)
	{
		this->registerSerializationSequence[n]->serialize(fs);
	}

	// overflow toggle
	fs->write((char*)&this->overflowToggle, sizeof(bool));

	// Comparison
	fs->write((char*)&this->comparison, sizeof(Comparison));
}


void CPU::execute(const Word* const instruction)
{
	#ifdef OUTPUTINSTRUCTIONS
	this->write_instruction(instruction);
	#endif

	unsigned int opCode = instruction->op_code();
	switch((OpCodes)opCode)
	{
		case MOVE:
			this->ticks += this->executionTimesAndNames[(OpCodes)opCode].first + (2 * instruction->modification_code());
			break;

		default:
			this->ticks += this->executionTimesAndNames[(OpCodes)opCode].first;
			break;
	}

	short instructionPointerOffset = 1;

	switch(opCode)
	{
		case NOP:
			break;

		case ADD:
		case SUB:
			this->execute_add_subtract(instruction);
			break;

		case MUL:
			this->multiply(instruction);
			break;

		case DIV:
			this->execute_divide(instruction);
			break;

		case NUM_CHAR_HLT:
			switch(instruction->modification_code())
			{
				case 2:			 // HLT, don't move forward
					throw CpuHalted();
					break;

				default:
					// CHAR or NUM is not yet implemented
					assert(false);
					break;
			}

			break;

		case SLA_SRA_SLAX_SRAX_SLC_SRC:
			switch(instruction->modification_code())
			{
				case 0:			 // SLA
					this->shift_left_a(instruction);
					break;

				case 1:			 // SRA
					this->shift_right_a(instruction);
					break;

				case 2:			 // SLAX
					this->shift_left_aX(instruction, false);
					break;

				case 3:			 // SRAX
					this->shift_right_aX(instruction, false);
					break;

				case 4:			 // SLC
					this->shift_left_aX(instruction, true);
					break;

				case 5:			 // SRC
					this->shift_right_aX(instruction, true);
					break;

				default:
					// SHIFT not yet implemented
					assert(false);
					break;
			}

			break;

		case MOVE:
			this->move(instruction);
			break;

		case LDA:
			this->load_register(&this->registerA, instruction, false);
			break;

		case LD1:
		case LD2:
		case LD3:
		case LD4:
		case LD5:
		case LD6:
			// NOTE: the -1 (LD1) is because we are zero-bound (and I1 is our zeroth)
			this->load_register(&this->registerI[instruction->op_code() - LD1], instruction, false);
			break;

		case LDX:
			this->load_register(&this->registerX, instruction, false);
			break;

		case LDAN:
			this->load_register(&this->registerA, instruction, true);
			break;

		case LD1N:
		case LD2N:
		case LD3N:
		case LD4N:
		case LD5N:
		case LD6N:
			// NOTE: the -1 (LD1N) is because we are zero-bound (and I1 is our zeroth)
			this->load_register(&this->registerI[instruction->op_code() - LD1N], instruction, true);
			break;

		case LDXN:
			this->load_register(&this->registerX, instruction, true);
			break;

		case STA:
			this->store_register(&this->registerA, instruction);
			break;

		case ST1:
		case ST2:
		case ST3:
		case ST4:
		case ST5:
		case ST6:
			// NOTE: the -1 (ST1) is because we are zero-bound (and I1 is our zeroth)
			this->store_index_register(&this->registerI[instruction->op_code() - ST1], instruction);
			break;

		case STX:
			this->store_register(&this->registerX, instruction);
			break;

		case STJ:
			this->store_index_register(&this->registerJ, instruction);
			break;

		case STZ:
			this->store_zero(instruction);
			break;

		case JMP_JSJ_JOV_JNOV_JL_JE_JG_JGE_JNE_JLE:
			this->handle_jump(instruction);
			instructionPointerOffset = 0;
			break;

		case JAN_JAZ_JAP_JANN_JANZ_JANP:
			this->handle_jump_on_register_status(instruction, &this->registerA);
			instructionPointerOffset = 0;
			break;

		case JXN_JXZ_JXP_JXNN_JXNZ_JXNP:
			this->handle_jump_on_register_status(instruction, &this->registerX);
			instructionPointerOffset = 0;
			break;

		case J1N_J1Z_J1P_J1NN_J1NZ_J1NP:
		case J2N_J2Z_J2P_J2NN_J2NZ_J2NP:
		case J3N_J3Z_J3P_J3NN_J3NZ_J3NP:
		case J4N_J4Z_J4P_J4NN_J4NZ_J4NP:
		case J5N_J5Z_J5P_J5NN_J5NZ_J5NP:
		case J6N_J6Z_J6P_J6NN_J6NZ_J6NP:
			this->handle_jump_on_register_status(instruction, &this->registerI[instruction->op_code() - J1N_J1Z_J1P_J1NN_J1NZ_J1NP]);
			instructionPointerOffset = 0;
			break;

		case INCA_DECA_ENTA_ENNA:
			this->handle_inc_dec_ent_enn(&this->registerA, instruction);
			break;

		case INC1_DEC1_ENT1_ENN1:
		case INC2_DEC2_ENT2_ENN2:
		case INC3_DEC3_ENT3_ENN3:
		case INC4_DEC4_ENT4_ENN4:
		case INC5_DEC5_ENT5_ENN5:
		case INC6_DEC6_ENT6_ENN6:
			// NOTE: the subtractions is because index registers are zero-bound (I1 is the zeroth)
			this->handle_inc_dec_ent_enn(&this->registerI[instruction->op_code() - INC1_DEC1_ENT1_ENN1], instruction);
			break;

		case INCX_DECX_ENTX_ENNX:
			this->handle_inc_dec_ent_enn(&this->registerX, instruction);
			break;

		case CMPA:
			this->handle_compare_register(&this->registerA, instruction);
			break;

		case CMP1:
		case CMP2:
		case CMP3:
		case CMP4:
		case CMP5:
		case CMP6:
			// NOTE: the -1 is because we are zero-bound (and I1 is our zeroth)
			this->handle_compare_index_register(&this->registerI[instruction->op_code() - CMP1], instruction);
			break;

		case CMPX:
			this->handle_compare_register(&this->registerX, instruction);
			break;

		default:
			assert(false);
			break;
	}

	this->instructionPointer += instructionPointerOffset;
}


unsigned char* CPU::combine_ax_registers() const
{
	int sizeA = this->registerA.get_size();
	int sizeX = this->registerX.get_size();

	unsigned char* buffer = new unsigned char[sizeA + sizeX];
	memcpy(buffer, this->registerA.value_bytes(), sizeA);
	memcpy(buffer + sizeA, this->registerX.value_bytes(), sizeX);

	return buffer;
}


void CPU::distribute_buffer_over_ax(const unsigned char* const buffer)
{
	int sizeA = this->registerA.get_size();
	int sizeX = this->registerX.get_size();

	memcpy(this->registerA.value_bytes(), buffer, sizeA);
	memcpy(this->registerX.value_bytes(), buffer + sizeA, sizeX);
}


void CPU::shift_left_a(const Word* const instruction)
{
	int value = instruction->address_value();
	if(value <= 0)
	{
		throw logic_error("Bytes to shift must be strictly positive");
	}

	unsigned char* buffer = this->registerA.value_bytes();
	this->shift_buffer_left(buffer, this->registerA.get_size(), value, false);
}


void CPU::shift_left_aX(const Word* const instruction, bool isCyclical)
{
	int value = instruction->address_value();
	if(value <= 0)
	{
		throw logic_error("Bytes to shift must be strictly positive");
	}

	int sizeA = this->registerA.get_size();
	int sizeX = this->registerX.get_size();

	unsigned char* buffer = this->combine_ax_registers();
	this->shift_buffer_left(buffer, sizeA + sizeX, value, isCyclical);
	this->distribute_buffer_over_ax(buffer);

	delete[] buffer;
}


void CPU::shift_right_aX(const Word* const instruction, bool isCyclical)
{
	int value = instruction->address_value();
	if(value <= 0)
	{
		throw logic_error("Bytes to shift must be strictly positive");
	}

	int sizeA = this->registerA.get_size();
	int sizeX = this->registerX.get_size();

	unsigned char* buffer = this->combine_ax_registers();
	this->shift_buffer_right(buffer, sizeA + sizeX, value, isCyclical);
	this->distribute_buffer_over_ax(buffer);

	delete[] buffer;
}


void CPU::shift_buffer_left(unsigned char* buffer, int bufferSize, int shiftAmount, bool isCyclical)
{
	for(int n = 0; n < shiftAmount; n++)
	{
		unsigned char overflow = buffer[0];
		for(int i = 0; i < bufferSize - 1; i++)
		{
			buffer[i] = buffer[i + 1];
		}

		buffer[bufferSize - 1] = isCyclical ? overflow : 0;
	}
}


void CPU::shift_buffer_right(unsigned char* buffer, int bufferSize, int shiftAmount, bool isCyclical)
{
	for(int n = 0; n < shiftAmount; n++)
	{
		unsigned char overflow = buffer[bufferSize - 1];
		for(int i = bufferSize - 1; i > 0; i--)
		{
			buffer[i] = buffer[i - 1];
		}

		buffer[0] = isCyclical ? overflow : 0;
	}
}


void CPU::shift_right_a(const Word* const instruction)
{
	int value = instruction->address_value();
	if(value <= 0)
	{
		throw logic_error("Bytes to shift must be strictly positive");
	}

	unsigned char* buffer = this->registerA.value_bytes();
	this->shift_buffer_right(buffer, this->registerA.get_size(), value, false);
}


void CPU::handle_jump_on_register_status(const Word* const instruction, const BaseWord* const targetRegister)
{
	switch(instruction->modification_code())
	{
		case 0:					 // Negative
			if(targetRegister->get_sign() < 0)
			{
				this->jump(instruction);
			}

			break;

		case 1:					 // Zero
			if(targetRegister->value() == 0)
			{
				this->jump(instruction);
			}

			break;

		case 2:					 // Positive
			if(targetRegister->get_sign() > 0)
			{
				this->jump(instruction);
			}
			break;

		case 3:					 // Non-Negative
			if(targetRegister->get_sign() >= 0)
			{
				this->jump(instruction);
			}
			break;

		case 4:					 // non-zero
			if(targetRegister->value() != 0)
			{
				this->jump(instruction);
			}

			break;

		case 5:					 // non-positive
			if(targetRegister->get_sign() <= 0)
			{
				this->jump(instruction);
			}
			break;

		default:
			assert(false);
	}
}


void CPU::handle_jump(const Word* const instruction)
{
	switch(instruction->modification_code())
	{
		case 0:					 // JMP
			this->jump(instruction);
			break;

		case 1:					 // JSJ
			this->jump_save_jump(instruction);
			break;

		case 2:					 // JOV
			this->jump_on_overflow(instruction);
			break;

		case 3:					 // JNOV
			this->jump_on_no_overflow(instruction);
			break;

		case 4:					 // JL
			this->jump_on_exact(Less, instruction);
			break;

		case 5:					 // JE
			this->jump_on_exact(Equal, instruction);
			break;

		case 6:					 // JG
			this->jump_on_exact(Greater, instruction);
			break;

		case 7:					 // JGE
			this->jump_on_any_condition(Greater, Equal, instruction);
			break;

		case 8:					 // JNE
			this->jump_on_any_condition(Greater, Less, instruction);
			break;

		case 9:					 // JLE
			this->jump_on_any_condition(Less, Equal, instruction);
			break;

		default:
			// Invalid Modification Code for jump instruction (JMP_JSJ_JOV_JNOV)
			assert(false);
			break;
	}
}


void CPU::handle_compare_index_register(const SmallWord* const targetRegister, const Word* const instruction)
{
	// NOTE: for registers; byte 1, 2, 3 are treated as zero (and so really only byte 4 and 5 exist)
	//       however, if I ask for fieldSpec 1:2; I should get +zero
	//       asking for 0:2 would get (+/-)zero (depending on the sign of the register)
	//       asking for 0:5 would get (+/-)addressValue (depending on the sign of the register)
	//       basically, we're treating the index register as if it looks like [+|-] 00 00 00 XX XX
	Word fullWord;
	fullWord.value(targetRegister->address_value());

	signed int leftOperand = fullWord.value_according_to_field_specification(instruction->modification_code());
	signed int rightOperand = this->pMemory[this->indexed_address_value(instruction)]->value_according_to_field_specification(instruction->modification_code());

	this->set_comparison_indicator(leftOperand, rightOperand);
}


void CPU::handle_compare_register(const Word* const targetRegister, const Word* const instruction)
{
	signed int leftOperand = targetRegister->value_according_to_field_specification(instruction->modification_code());
	signed int rightOperand = this->pMemory[this->indexed_address_value(instruction)]->value_according_to_field_specification(instruction->modification_code());

	this->set_comparison_indicator(leftOperand, rightOperand);
}


void CPU::jump(const Word* const instruction)
{
	convert_to_mix_bytes(this->instructionPointer + 1, this->registerJ.value_bytes(), SmallWord::WORD_SIZE);
	this->jump_save_jump(instruction);
}


void CPU::jump_on_exact(Comparison condition, const Word* const instruction)
{
	if(this->comparison == condition)
	{
		this->jump(instruction);
	}
	else
	{
		this->instructionPointer++;
	}
}


void CPU::jump_on_any_condition(Comparison first, Comparison second, const Word* const instruction)
{
	if(this->comparison == first || this->comparison == second)
	{
		this->jump(instruction);
	}
	else
	{
		this->instructionPointer++;
	}
}


void CPU::jump_on_no_overflow(const Word* const instruction)
{
	if(this->overflowToggle == false)
	{
		this->jump(instruction);
	}
	else
	{
		this->overflowToggle = false;
		this->instructionPointer++;
	}
}


void CPU::jump_on_overflow(const Word* const instruction)
{
	if(this->overflowToggle == true)
	{
		this->overflowToggle = false;
		this->jump(instruction);
	}
	else
	{
		this->instructionPointer++;
	}
}


void CPU::jump_save_jump(const Word* const instruction)
{
	this->instructionPointer = instruction->address_value();
}


void CPU::set_comparison_indicator(signed int registerValue, signed int otherValue)
{
	if(registerValue < otherValue)
	{
		this->comparison = Less;
	}
	else if(registerValue > otherValue)
	{
		this->comparison = Greater;
	}
	else
	{
		this->comparison = Equal;
	}
}


void CPU::execute_divide(const Word* const instruction)
{
	// Take rAX (10 byte value of rA and rX combined)
	// divide it with VALUE(M[+I]) of instruction (=V)
	// if V is 0 or quotient > 5 bytes (equivalent to |rA| >= |V|); set overflow toggle
	// Quotient (rAX / V) is placed in rA (with sign of quotient)
	// Remainder (rAX mod V) is placed in rX (with previous sign of rA, as before starting the operation)
	long long rxa = this->registerA.value_according_to_field_specification(L1_R5);
	for(short n = 0; n < this->registerX.get_size(); n++)
	{
		rxa *= (long long)Word::DISTINCT_BYTE_VALUES;
	}

	rxa += (long long)this->registerX.value_according_to_field_specification(L1_R5);
	rxa *= (long long)this->registerA.get_sign();

	long long divider = this->pMemory[this->indexed_address_value(instruction)]->value_according_to_field_specification(instruction->modification_code());

	try
	{
		if(divider == 0)
		{
			throw new overflow_error("execute_divide by zero");
		}

		// |rA| >= |V|
		if(abs(this->registerA.value_according_to_field_specification(L0_R5)) >= abs(divider))
		{
			throw new overflow_error("Division overflow");
		}

		signed int quotient = (signed int)(rxa / divider);
		signed int mod = (signed int)(rxa % divider);

		this->registerX.value(mod);

		// rX gets the original sign of rA
		this->registerX.set_sign(this->registerA.get_sign());

		this->registerA.value(quotient);
	}
	catch(overflow_error)
	{
		this->overflowToggle = true;
	}
}


void CPU::multiply(const Word* const instruction)
{
	// multiply rA with the CONTENTS(M[+I]) of the instruction
	// left == rA
	// right == CONTENTS(M[+I])
	signed long long left = this->registerA.value_according_to_field_specification(L0_R5);
	signed long long right = this->pMemory[this->indexed_address_value(instruction)]->value_according_to_field_specification(instruction->modification_code());

	try
	{
		signed char sign = (left >= 0) == (right >= 0) ? +1 : -1;
		long long result = abs(left * right);

		// result is not absolute value
		assert(result >= 0);

		// How to break up 'result' into rA and rX?
		const int BUFFER_SIZE = 10;
		unsigned char buffer[BUFFER_SIZE];
		convert_to_mix_bytes(result, buffer, BUFFER_SIZE);

		this->registerA.value(buffer);
		this->registerA.set_sign(sign);

		this->registerX.value(buffer + (BUFFER_SIZE / 2));
		this->registerX.set_sign(sign);
	}
	catch(overflow_error)
	{
		// TODO, thomasde: should we report the overflow?
		this->overflowToggle = true;
	}
}


void CPU::move(const Word* const instruction)
{
	int wordsToMove = instruction->modification_code();
	int startLocation = instruction->address_value();
	int targetLocation = this->registerI[0].value();

	Word** buffer = new Word*[wordsToMove];
	Word* src;
	for(int n = 0; n < wordsToMove; n++)
	{
		src = this->pMemory[startLocation + n];
		*(buffer + n) = new Word(src->get_sign(), src->value_bytes());
	}

	for(int n = 0; n < wordsToMove; n++)
	{
		this->pMemory[targetLocation + n]->value(buffer[n]->value());
	}

	this->registerI[0].value(this->registerI[0].value() + wordsToMove);
}


void CPU::handle_inc_dec_ent_enn(BaseWord* const targetRegister, const Word* const instruction)
{
	int modificationCode = instruction->modification_code();
	switch(modificationCode)
	{
		case 0:					 // INCx
		case 1:					 // DECx
		{
			signed short valueToAdd = instruction->address_value();

			// is this an INCrease (modCode == 0) or a DECrease (modCode == 1)?
			if(modificationCode == 1)
			{
				valueToAdd = -valueToAdd;
			}

			signed int newValue = targetRegister->value_according_to_field_specification(0, targetRegister->get_size()) + valueToAdd;

			try
			{
				targetRegister->value(newValue);
			}
			catch(overflow_error)
			{
				this->overflowToggle = true;
			}

			break;
		}

		case 2:					 // ENT<x>
			this->execute_enter_register(targetRegister, instruction, false);
			break;

		case 3:					 // ENN<x>
			this->execute_enter_register(targetRegister, instruction, true);
			break;

		default:
			// invalid modification code for INCX_DECX_ENTX_ENNX
			assert(false);
			break;
	}
}


void CPU::execute_enter_register(BaseWord* const targetRegister, const Word* const instruction, bool negative)
{
	// are we using the address value (M) of the instruction or an index?
	signed short value;
	unsigned char indexSpec = instruction->index_specification();
	if(indexSpec == 0)
	{
		// use M
		value = instruction->address_value();
	}
	else
	{
		// Invalid index value
		assert(indexSpec >= 1 && indexSpec <= 6);
		value = this->registerI[indexSpec].address_value();
	}

	if(negative)
	{
		value = -value;
	}

	targetRegister->value(value);
}


void CPU::execute_add_subtract(const Word* const instruction)
{
	// from indexed(content(M)); read the value of the specified fields (V) and add this to register A
	unsigned short address = this->indexed_address_value(instruction);

	unsigned short left;
	unsigned short right;
	instruction->unwrap_modification_code(left, right);

	try
	{
		signed int value;
		if(instruction->op_code() == ADD)
		{
			value = this->registerA.value_according_to_field_specification(L0, R5) + this->pMemory[address]->value_according_to_field_specification(left, right);
		}
		else if(instruction->op_code() == SUB)
		{
			value = this->registerA.value_according_to_field_specification(L0, R5) - this->pMemory[address]->value_according_to_field_specification(left, right);
		}
		else
		{
			// Unsupported OpCode in execute_add_subtract
			assert(false);
		}

		this->registerA.value(value);
	}
	catch(overflow_error)
	{
		// TODO, thomasde: should we report the overflow?
		this->overflowToggle = true;
	}
}


unsigned int CPU::indexed_address_value(const Word* const instruction)
{
	unsigned short result = instruction->address_value();
	this->update_address_with_index(result, instruction);

	return result;
}


void CPU::update_address_with_index(unsigned short &address, const Word* const instruction)
{
	unsigned short index = instruction->index_specification();
	if(index != 0)
	{
		assert(index >= 1 && index <= 6);
		SmallWord* indexRegister = &this->registerI[index - 1];
		address += indexRegister->address_value();
	}
}


void CPU::load_register(BaseWord* const targetRegister, const Word* const instruction, bool negative)
{
	// LD[A|X|i] ADDRESS,I(F)
	// F == (0:5) : load the entirety of ADDRESS into register
	// F == (1:5) : load the entirety of ADDRESS into register with sign +
	unsigned short left, right;
	instruction->unwrap_modification_code(left, right);

	unsigned short address = this->indexed_address_value(instruction);

	signed int value = this->pMemory[address]->value_according_to_field_specification(left, right);
	targetRegister->value(negative ? -value : value);
}


void CPU::store_zero(const Word* const instruction)
{
	unsigned short left, right;
	instruction->unwrap_modification_code(left, right);

	unsigned short address = this->indexed_address_value(instruction);

	Word* const storeTo = this->pMemory[address];
	while(right >= left && right > 0)
	{
		storeTo->value_bytes()[right - 1] = 0;
		right--;
	}

	// include sign?
	if(left == 0)
	{
		storeTo->set_sign(+1);
	}
}


void CPU::store_index_register(const SmallWord* const targetRegister, const Word* const instruction)
{
	// NOTE: Byte 1, 2, 3 are considered null so we have to convert the SmallWord into a regular word
	Word fakeRegister;
	fakeRegister.value(targetRegister->address_value());

	this->store_register(&fakeRegister, instruction);
}


void CPU::store_register(const Word* const targetRegister, const Word* const instruction)
{
	// ST[A|X|i] ADDRESS,I(F)
	// stores (right - left + 1) bytes into the location (left:right) of the target
	unsigned short left, right;
	instruction->unwrap_modification_code(left, right);

	Word* const storeTo = this->pMemory[this->indexed_address_value(instruction)];
	unsigned short targetRegisterIndex = targetRegister->get_size() - 1;
	while(right >= left && right > 0)
	{
		assert(targetRegisterIndex >= 0);
		storeTo->value_bytes()[right - 1] = targetRegister->value_bytes()[targetRegisterIndex];

		right--;
		targetRegisterIndex--;
	}

	// include sign?
	if(left == 0)
	{
		storeTo->set_sign(targetRegister->get_sign());
	}
}


void CPU::populate_execution_times()
{
	string* names = new string[1];
	names[0] = "NOP";
	this->executionTimesAndNames[NOP] = pair<unsigned int, string*>(1, names);

	names = new string[1];
	names[0] = "ADD";
	this->executionTimesAndNames[ADD] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "SUB";
	this->executionTimesAndNames[SUB] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "MUL";
	this->executionTimesAndNames[MUL] = pair<unsigned int, string*>(10, names);

	names = new string[1];
	names[0] = "DIV";
	this->executionTimesAndNames[DIV] = pair<unsigned int, string*>(12, names);

	names = new string[3];
	names[0] = "NUM";
	names[1] = "CHAR";
	names[2] = "HLT";
	this->executionTimesAndNames[NUM_CHAR_HLT] = pair<unsigned int, string*>(10, names);

	names = new string[6];
	names[0] = "SLA";
	names[1] = "SRA";
	names[2] = "SLAX";
	names[3] = "SRAX";
	names[4] = "SLC";
	names[5] = "SRC";
	this->executionTimesAndNames[SLA_SRA_SLAX_SRAX_SLC_SRC] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "MOVE";
	this->executionTimesAndNames[MOVE] = pair<unsigned int, string*>(1, names);

	names = new string[1];
	names[0] = "LDA";
	this->executionTimesAndNames[LDA] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD1";
	this->executionTimesAndNames[LD1] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD2";
	this->executionTimesAndNames[LD2] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD3";
	this->executionTimesAndNames[LD3] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD4";
	this->executionTimesAndNames[LD4] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD5";
	this->executionTimesAndNames[LD5] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD6";
	this->executionTimesAndNames[LD6] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LDX";
	this->executionTimesAndNames[LDX] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LDAN";
	this->executionTimesAndNames[LDAN] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD1N";
	this->executionTimesAndNames[LD1N] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD2N";
	this->executionTimesAndNames[LD2N] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD3N";
	this->executionTimesAndNames[LD3N] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD4N";
	this->executionTimesAndNames[LD4N] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD5N";
	this->executionTimesAndNames[LD5N] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LD6N";
	this->executionTimesAndNames[LD6N] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "LDXN";
	this->executionTimesAndNames[LDXN] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "STA";
	this->executionTimesAndNames[STA] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "ST1";
	this->executionTimesAndNames[ST1] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "ST2";
	this->executionTimesAndNames[ST2] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "ST3";
	this->executionTimesAndNames[ST3] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "ST4";
	this->executionTimesAndNames[ST4] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "ST5";
	this->executionTimesAndNames[ST5] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "ST6";
	this->executionTimesAndNames[ST6] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "STX";
	this->executionTimesAndNames[STX] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "STJ";
	this->executionTimesAndNames[STJ] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "STZ";
	this->executionTimesAndNames[STZ] = pair<unsigned int, string*>(2, names);

	names = new string[10];
	names[0] = "JMP";
	names[1] = "JSJ";
	names[2] = "JOV";
	names[3] = "JNOV";
	names[4] = "JL";
	names[5] = "JE";
	names[6] = "JG";
	names[7] = "JGE";
	names[8] = "JNE";
	names[9] = "JLE";
	this->executionTimesAndNames[JMP_JSJ_JOV_JNOV_JL_JE_JG_JGE_JNE_JLE] = pair<unsigned int, string*>(1, names);

	names = new string[6];
	names[0] = "JAN";
	names[1] = "JAZ";
	names[2] = "JAP";
	names[3] = "JANN";
	names[4] = "JANZ";
	names[5] = "JANP";
	this->executionTimesAndNames[JAN_JAZ_JAP_JANN_JANZ_JANP] = pair<unsigned int, string*>(1, names);

	names = new string[6];
	names[0] = "J1N";
	names[1] = "J1Z";
	names[2] = "J1P";
	names[3] = "J1NN";
	names[4] = "J1NZ";
	names[5] = "J1NP";
	this->executionTimesAndNames[J1N_J1Z_J1P_J1NN_J1NZ_J1NP] = pair<unsigned int, string*>(1, names);

	names = new string[6];
	names[0] = "J2N";
	names[1] = "J2Z";
	names[2] = "J2P";
	names[3] = "J2NN";
	names[4] = "J2NZ";
	names[5] = "J2NP";
	this->executionTimesAndNames[J2N_J2Z_J2P_J2NN_J2NZ_J2NP] = pair<unsigned int, string*>(1, names);

	names = new string[6];
	names[0] = "J3N";
	names[1] = "J3Z";
	names[2] = "J3P";
	names[3] = "J3NN";
	names[4] = "J3NZ";
	names[5] = "J3NP";
	this->executionTimesAndNames[J3N_J3Z_J3P_J3NN_J3NZ_J3NP] = pair<unsigned int, string*>(1, names);

	names = new string[6];
	names[0] = "J4N";
	names[1] = "J4Z";
	names[2] = "J4P";
	names[3] = "J4NN";
	names[4] = "J4NZ";
	names[5] = "J4NP";
	this->executionTimesAndNames[J4N_J4Z_J4P_J4NN_J4NZ_J4NP] = pair<unsigned int, string*>(1, names);

	names = new string[6];
	names[0] = "J5N";
	names[1] = "J5Z";
	names[2] = "J5P";
	names[3] = "J5NN";
	names[4] = "J5NZ";
	names[5] = "J5NP";
	this->executionTimesAndNames[J5N_J5Z_J5P_J5NN_J5NZ_J5NP] = pair<unsigned int, string*>(1, names);

	names = new string[6];
	names[0] = "J6N";
	names[1] = "J6Z";
	names[2] = "J6P";
	names[3] = "J6NN";
	names[4] = "J6NZ";
	names[5] = "J6NP";
	this->executionTimesAndNames[J6N_J6Z_J6P_J6NN_J6NZ_J6NP] = pair<unsigned int, string*>(1, names);

	names = new string[6];
	names[0] = "JXN";
	names[1] = "JXZ";
	names[2] = "JXP";
	names[3] = "JXNN";
	names[4] = "JXNZ";
	names[5] = "JXNP";
	this->executionTimesAndNames[JXN_JXZ_JXP_JXNN_JXNZ_JXNP] = pair<unsigned int, string*>(1, names);

	names = new string[4];
	names[0] = "INCA";
	names[1] = "DECA";
	names[2] = "ENTA";
	names[3] = "ENNA";
	this->executionTimesAndNames[INCA_DECA_ENTA_ENNA] = pair<unsigned int, string*>(1, names);

	names = new string[4];
	names[0] = "INC1";
	names[1] = "DEC1";
	names[2] = "ENT1";
	names[3] = "ENN1";
	this->executionTimesAndNames[INC1_DEC1_ENT1_ENN1] = pair<unsigned int, string*>(1, names);

	names = new string[4];
	names[0] = "INC2";
	names[1] = "DEC2";
	names[2] = "ENT2";
	names[3] = "ENN2";
	this->executionTimesAndNames[INC2_DEC2_ENT2_ENN2] = pair<unsigned int, string*>(1, names);

	names = new string[4];
	names[0] = "INC3";
	names[1] = "DEC3";
	names[2] = "ENT3";
	names[3] = "ENN3";
	this->executionTimesAndNames[INC3_DEC3_ENT3_ENN3] = pair<unsigned int, string*>(1, names);

	names = new string[4];
	names[0] = "INC4";
	names[1] = "DEC4";
	names[2] = "ENT4";
	names[3] = "ENN4";
	this->executionTimesAndNames[INC4_DEC4_ENT4_ENN4] = pair<unsigned int, string*>(1, names);

	names = new string[4];
	names[0] = "INC5";
	names[1] = "DEC5";
	names[2] = "ENT5";
	names[3] = "ENN5";
	this->executionTimesAndNames[INC5_DEC5_ENT5_ENN5] = pair<unsigned int, string*>(1, names);

	names = new string[4];
	names[0] = "INC6";
	names[1] = "DEC6";
	names[2] = "ENT6";
	names[3] = "ENN6";
	this->executionTimesAndNames[INC6_DEC6_ENT6_ENN6] = pair<unsigned int, string*>(1, names);

	names = new string[4];
	names[0] = "INCX";
	names[1] = "DECX";
	names[2] = "ENTX";
	names[3] = "ENNX";
	this->executionTimesAndNames[INCX_DECX_ENTX_ENNX] = pair<unsigned int, string*>(1, names);

	names = new string[1];
	names[0] = "CMPA";
	this->executionTimesAndNames[CMPA] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "CMP1";
	this->executionTimesAndNames[CMP1] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "CMP2";
	this->executionTimesAndNames[CMP2] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "CMP3";
	this->executionTimesAndNames[CMP3] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "CMP4";
	this->executionTimesAndNames[CMP4] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "CMP5";
	this->executionTimesAndNames[CMP5] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "CMP6";
	this->executionTimesAndNames[CMP6] = pair<unsigned int, string*>(2, names);

	names = new string[1];
	names[0] = "CMPX";
	this->executionTimesAndNames[CMPX] = pair<unsigned int, string*>(2, names);
}


unsigned int CPU::Ticks()
{
	return this->ticks;
}


#ifdef OUTPUTINSTRUCTIONS
void CPU::write_instruction(const Word* const instruction, const int address)
{
	unsigned short opcode = instruction->op_code();
	bool omitFieldSpec = false;
	unsigned short nameIndex = 0;
	switch(opcode)
	{
		case NUM_CHAR_HLT:
		case SLA_SRA_SLAX_SRAX_SLC_SRC:
		case JMP_JSJ_JOV_JNOV_JL_JE_JG_JGE_JNE_JLE:
		case INCA_DECA_ENTA_ENNA:
		case INC1_DEC1_ENT1_ENN1:
		case INC2_DEC2_ENT2_ENN2:
		case INC3_DEC3_ENT3_ENN3:
		case INC4_DEC4_ENT4_ENN4:
		case INC5_DEC5_ENT5_ENN5:
		case INC6_DEC6_ENT6_ENN6:
		case INCX_DECX_ENTX_ENNX:
			nameIndex = instruction->modification_code();
			omitFieldSpec = true;
			break;

		default:
			break;
	}

	cout << "0x" << std::hex << std::setfill('0') << std::setw(ADDRESS_WIDTH) << (address < 0 ? this->instruction_pointer() : address) << " ";
	cout << setw(4) << setfill(' ') << this->executionTimesAndNames[(OpCodes)(instruction->op_code())].second[nameIndex].data() << " ";

	signed char sign = instruction->get_sign();
	cout << (sign > 0 ? '+' : (sign < 0 ? '-' : '~')) << setw(2) << setfill('0') << hex << (int)(instruction->value_bytes()[0]) << setw(2) << setfill('0') << hex << (int)(instruction->value_bytes()[1]);

	unsigned short index = instruction->index_specification();

	// invalid index
	assert(index >= 0 && index <= 6);
	if(index != 0)
	{
		cout << " " << index;
	}

	if(!omitFieldSpec)
	{
		unsigned short left, right;
		instruction->unwrap_modification_code(left, right);
		cout << "(" << left << ":" << right << ")";
	}

	cout << endl;
}
#endif
